#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include <iostream>
#include <cstdio>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <unordered_map>
#include "log.hpp"

#define SIZE 1024
class UdpServer
{
public:
    UdpServer(uint16_t port, std::string ip = ""):_port(port),_ip(ip)
    {}

    bool InitServer()
    {
        // 从这里开始，就是新的系统调用，来完成网络功能啦
        //  1. 创建套接字
        //第一个参数代表域，AF_UNIX本地域，AF_INET网络域，第二个参数代表通信种类SOCK_DGRAM数据报通信方式
        //第三个参数一般写0
        _sock = socket(AF_INET,SOCK_DGRAM,0);

        if(_sock < 0)
        {
            logMessage(FATAL,"%d:%s",errno,strerror(errno));
            exit(2);
        }
        // 2. bind: 将用户设置的ip和port在内核中和我们当前的进程强关联
        // "192.168.110.132" -> 点分十进制字符串风格的IP地址
        // 每一个区域取值范围是[0-255]: 1字节 -> 4个区域
        // 理论上，表示一个IP地址，其实4字节就够了！！
        // 点分十进制字符串风格的IP地址 <-> 4字节
        struct sockaddr_in local;
        memset(&local,0,sizeof (local));//对空间初始化
        local.sin_family = AF_INET;
        // 服务器的IP和端口未来也是要发送给对方主机的 -> 先要将数据发送到网络！
        local.sin_port = htons(_port);
        // 1. 同上，先要将点分十进制字符串风格的IP地址 ->  4字节
        // 2.  4字节主机序列 -> 网络序列
        // 有一套接口，可以一次帮我们做完这两件事情, 让服务器在工作过程中，可以从任意IP中获取数据
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        //细节，需要强转
        if(bind(_sock,(sockaddr*)&local,sizeof(local)) < 0)
        {
            logMessage(FATAL,"%d:%s",errno,strerror(errno));
            exit(2);
        } 
        logMessage(NORMAL,"init udp server done...%s",strerror(errno));
        //done
        return true;    
    }

    void Start()
    {
        //作为一款网络服务器，是永远不退出的！
        //服务器启动->进程->常驻进程->永远再内存中存在，除非挂了
        char buffer[SIZE];
        for( ; ; )
        {
            //注意：
            //peer，纯输出型参数
            struct sockaddr_in peer;
            bzero(&peer,sizeof(peer));//初始化
            //输入：peer缓冲区大小
            //输出：实际读到的peer
            socklen_t len = sizeof(peer);
            char result[512];
            char key[64];
            std::string cmd_echo;
            //start，读取数据
            ssize_t s = recvfrom(_sock,buffer,sizeof(buffer) - 1, 0, (sockaddr*)&peer, &len);
            if(s > 0)
            {
                buffer[s] = 0;//我们目前的数据当作字符串
                //1.输出发送的信息
                //是谁？
                //执行命令方式
                // FILE* fp = popen(buffer,"r");
                // if(nullptr == fp)
                // {
                //     logMessage(ERROR,"popen:%d:%s\n",errno,strerror(errno));//不是致命错误
                //     continue;
                // }
                // while(fgets(result,sizeof(result), fp))
                // {
                //     cmd_echo += result;
                // }
                // fclose(fp);

                uint16_t cli_port = ntohs(peer.sin_port);//从网络中来，要转为本地
                std::string cli_ip = inet_ntoa(peer.sin_addr);//4字节的网络序列IP->转为本主机字符串风格的IP
                snprintf(key,sizeof(key), "%s-%u", cli_ip.c_str(),cli_port);
                logMessage(NORMAL,"key: %s",key);
                auto it = _user.find(key);
                if(it == _user.end())
                {
                    // exists
                    logMessage(NORMAL, "add new user : %s", key);
                    _user.insert({key, peer});
                }
                // printf("[%s:%d]# %s\n",cli_ip.c_str(),cli_port,buffer);
            }
            //分析和处理数据
            //end，写回数据
            // sendto(_sock, buffer, sizeof(buffer), 0, (sockaddr*)&peer, len);
            for(auto& it : _user)
            {
                std::string sendMessage = key;
                sendMessage += "# ";
                sendMessage += buffer;
                logMessage(NORMAL,"push message to %s",it.first.c_str());
                sendto(_sock, sendMessage.c_str(), sendMessage.size(), 0, (sockaddr*)&(it.second), sizeof(it.second));
            }
            
        }
    }

    ~UdpServer()
    {
        if(_sock >= 0) close(_sock);
    }
private:
    uint16_t _port;
    std::string _ip;
    int _sock;
    std::unordered_map<std::string,struct sockaddr_in> _user;
};
#endif